Skip to content

Add settings tooltips and search to the web UI#387

Merged
ChuckBuilds merged 9 commits into
mainfrom
claude/web-ui-tooltips-search-36s3b9
Jul 9, 2026
Merged

Add settings tooltips and search to the web UI#387
ChuckBuilds merged 9 commits into
mainfrom
claude/web-ui-tooltips-search-36s3b9

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Pull Request

Summary

Adds two features to the v3 web UI so users can quickly find settings and understand how each one works: an accessible (i) info tooltip on every setting, and a search — a global header box that finds settings across all tabs (even unopened ones) plus a per-tab filter. Help text that used to sit under each field is folded into the tooltip to declutter the forms, and hardware/display settings carry authored detail (default, range, recommendation).

Type of change

  • New feature

Related issues

Test plan

  • Ran on a real Raspberry Pi with hardware
  • Ran in emulator mode (EMULATOR=true python3 run.py)
  • Ran the dev preview server (scripts/dev_server.py)
  • Ran the test suite (pytest)
  • Manually verified the affected code path in the web UI

Details:

  • New Flask render smoke tests (test/test_web_settings_ui.py, 11 cases) assert each settings partial ships tooltips (class="help-tip"), search anchors (id="setting-..."), and a per-tab filter box, and that authored rich detail is present — all pass.
  • Rendered every settings partial and the full base.html shell through Flask (200s; header search box + script includes present).
  • Drove the compiled assets in headless Chromium (Playwright): tooltip shows on hover with the right text and aria-describedby, hides on mouse-out; the per-tab filter hides/shows fields; the global search builds its index, shows a tab-grouped dropdown, and selecting a result switches tabs, scrolls to the field, and flashes it.
  • node --check passes on both new JS modules; all templates compile.

Documentation

  • I updated README.md if user-facing behavior changed
  • I updated the relevant doc in docs/ if developer behavior changed
  • I added/updated docstrings on new public functions
  • N/A — no docs needed

Help text shown in the new tooltips for the Display/hardware settings was sourced from the existing per-key reference in README.md; no README change was needed.

Plugin compatibility

  • No plugin breakage expected

Plugin settings get tooltips for free by reusing each field's existing schema description in the plugin_config.html macro; no plugin or schema changes are required. The plugin enabled key remains rendered as the header toggle (unchanged).

Checklist

  • My commits follow the message convention in CONTRIBUTING.md
  • I read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • I've not committed any secrets or hardcoded API keys
  • If this adds a new config key, the form in the web UI was verified (no new config keys added)

Notes for reviewer

How it works

  • partials/_macros.html — shared Jinja macros: help_tip(text, label) emits an accessible <button class="help-tip" data-tooltip="…">; fg_id(tab, key) for anchor ids; settings_filter() for the per-tab filter box.
  • static/v3/js/tooltips.js — one delegated controller for all .help-tip triggers (hover, keyboard focus via :focus-visible, tap toggle, Esc/outside-click to close). Survives HTMX swaps with no re-init; positions a single #ledm-tooltip panel; text set via textContent (XSS-safe) with white-space: pre-line for authored \n line breaks.
  • static/v3/js/settings-search.js — lazy, session-cached client index built by fetching each settings partial once and scanning the same .form-group[id^="setting-"] + <label> + data-tooltip markup, so it can't drift from what's rendered. Global dropdown (grouped by tab, full keyboard nav) + navigateToSetting (sets Alpine activeTab, MutationObserver-waits for the field to appear — handling the two-stage plugin load — then scrolls + flashes). Per-tab filter is a delegated input handler scoped to the active tab.
  • Styling in app.css uses the existing --color-* theme vars (light/dark automatic) and honors prefers-reduced-motion.

Decisions worth a look

  • Per the product direction, always-visible field help paragraphs were removed and folded into the tooltip. Section/page-level descriptions were intentionally kept visible.
  • Only true settings tabs are indexed for global search (General, Display, Schedule, WiFi). Operational tabs (Cache, Tools, Fonts, Backup/Restore) have no settings fields, so they're excluded — though Backup/Restore's export options got explanatory tooltips.
  • Follow-up candidates (out of scope here): tooltips for the Starlark app config form and the font-override builder controls.

🤖 Generated with Claude Code

https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added a global settings search in the v3 interface, with dropdown results grouped by tab and the ability to jump directly to the matching field.
    • Added per-tab filter boxes to quickly narrow the visible settings within each settings section.
    • Introduced consistent contextual help tooltips across many settings (with an improved global tooltip panel).
  • Bug Fixes

    • Improved search and navigation reliability using stable setting anchors and better field highlighting after jumping.
    • Enhanced tooltip interactions for mouse, keyboard, and touch, including correct behavior after dynamic page updates.

Help users quickly find settings and understand how each one works.

Tooltips: a new delegated controller (static/v3/js/tooltips.js) drives an
accessible (i) info tooltip that appears on hover, keyboard focus, and tap.
A shared `help_tip` Jinja macro (partials/_macros.html) emits the trigger;
the plugin config macro and the core settings partials now surface help
text through it. Per the design, the always-visible field help paragraphs
are folded into the tooltip to declutter the forms, and the hardware/display
settings carry authored detail (default, range, recommendation).

Search: a global header search box finds settings across every settings tab
— even ones not yet opened — via a lazy client-side index built by scanning
the same field markup (static/v3/js/settings-search.js). Selecting a result
switches tabs, waits for the field to load, then scrolls to and flashes it.
A per-tab filter box hides non-matching fields on the current tab.

Plugin settings get tooltips for free by reusing each field's schema
`description`; every settings field also gets a stable `setting-<tab>-<key>`
anchor id for search navigation.

Styling uses the existing --color-* theme vars so light/dark mode both work,
and honors prefers-reduced-motion. Adds Flask render smoke tests that assert
each settings partial ships tooltips, anchors, and a filter box.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ChuckBuilds, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b2600fb0-7647-407c-a15f-6605ace45f9e

📥 Commits

Reviewing files that changed from the base of the PR and between 497a103 and b38a27e.

📒 Files selected for processing (3)
  • test/test_web_settings_ui.py
  • web_interface/static/v3/js/settings-search.js
  • web_interface/templates/v3/partials/plugin_config.html
📝 Walkthrough

Walkthrough

Adds a v3 settings search and tooltip system: shared template macros and styling, a server-built searchable index, client-side search/filter behavior, header wiring, broader partial markup updates, and smoke tests covering anchors, tooltips, and the search index.

Changes

Settings Search and Tooltip UI

Layer / File(s) Summary
Shared UI macros
web_interface/templates/v3/partials/_macros.html
Adds help_tip, fg_id, and settings_filter macros for tooltips, stable anchor ids, and per-tab filter inputs.
Display partial anchors and tooltips
web_interface/templates/v3/partials/display.html
Wraps hardware, PWM, double-sided, options, duration, vegas-scroll, and sync fields with id/data-setting-key, replaces descriptions with tooltips, and updates updateSyncUI() to use the new element id.
Other settings partials
web_interface/templates/v3/partials/general.html, durations.html, schedule.html, wifi.html, backup_restore.html, plugin_config.html
Applies shared macros to add stable anchors, data-setting-key attributes, help tooltips, and settings_filter controls across all remaining settings tabs.
Tooltip and search styling
web_interface/static/v3/app.css
Adds .help-tip, #ledm-tooltip panel with fade animation, #settings-search-results dropdown styling, and .setting-flash highlight with reduced-motion overrides.
Tooltip controller script
web_interface/static/v3/js/tooltips.js
Adds a delegated tooltip controller handling hover, focus, click, escape, scroll/resize, and HTMX swap events via a single reusable panel.
Search index backend endpoint
web_interface/blueprints/pages_v3.py
Adds an HTML parser, partial-render helper, plugin-signature-keyed cache, and /settings/search-index route returning flattened searchable field metadata.
Global search and per-tab filter script
web_interface/static/v3/js/settings-search.js
Adds index fetching/caching, dropdown search rendering with keyboard/mouse navigation, navigation-and-flash to selected fields, and delegated per-tab filtering.
Header search UI and script wiring
web_interface/templates/v3/base.html
Adds a global search input/results container to the header and loads tooltips.js/settings-search.js as deferred scripts.
Settings UI smoke tests
test/test_web_settings_ui.py
Adds tests validating anchors, tooltips, per-tab filters, brightness tooltip content, and the search-index endpoint response.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SettingsSearchJS as settings-search.js
  participant PagesV3 as pages_v3 /settings/search-index
  participant DOM

  User->>SettingsSearchJS: type query in header search
  SettingsSearchJS->>PagesV3: fetch /v3/settings/search-index
  PagesV3-->>SettingsSearchJS: fields JSON (cached or built)
  SettingsSearchJS->>SettingsSearchJS: search(q) match terms
  SettingsSearchJS->>DOM: renderResults grouped by tab
  User->>SettingsSearchJS: select result
  SettingsSearchJS->>DOM: setActiveTab, waitForElement, reveal section
  SettingsSearchJS->>DOM: scroll to field and flash
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: settings tooltips and search in the web UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/web-ui-tooltips-search-36s3b9

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Jul 8, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 273 complexity · 2 duplication

Metric Results
Complexity 273
Duplication 2

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

claude added 4 commits July 8, 2026 15:13
Refactor the two new modules to clear the flagged patterns without any
behavior change:

- Build the search dropdown with DOM nodes + textContent instead of
  innerHTML string concatenation, removing the XSS sinks and the manual
  escapeHtml helper it needed.
- Replace numeric index access (index[i], terms[j], opts[idx],
  currentResults[i]) with array iteration methods, NodeList.item(), and
  Array.prototype.at() to clear detect-object-injection.
- Use === via a shared termsMatch() helper, optional-catch binding, and
  drop a useless initial assignment.

Verified with ESLint (eslint:recommended + eslint-plugin-security) at zero
findings and re-ran the headless-Chromium behavior test (tooltip, per-tab
filter, global search navigation) — all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
Extract isNodeHidden() and revealNode() helpers so revealAncestors drops
below the cyclomatic-complexity threshold. No behavior change; verified with
the headless-Chromium test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
- Validate plugin ids against a strict allowlist (mirroring the server's
  _SAFE_PLUGIN_ID_RE) before they can appear in a fetch path, so the request
  URL is never built from unvalidated input (Codacy: user-controlled URL).
- Document that the fetched HTML is parsed into an inert document (scripts
  never run, never inserted into the live DOM) purely to read field text for
  the search index.
- Declare block-scoped locals with const instead of var where they were
  nested inside conditionals (Codacy: var not at function root).

No behavior change; re-verified with ESLint and the headless-Chromium test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
Move index building off the client so there is no client-side HTML fetching
or DOM parsing (resolves Codacy's variable-fetch and DOMParser flags on the
read-only, same-origin index build).

- Add GET /v3/settings/search-index (pages_v3.py): renders the settings
  partials server-side and extracts each field's anchor id, key, label,
  tooltip, and section with a small stdlib HTMLParser, then caches the result
  keyed on the installed-plugin set. Parsing the rendered HTML keeps anchor
  ids identical to the live DOM, so the index cannot drift.
- settings-search.js: buildIndex() now does a single fetch of the literal
  endpoint + .json(); removed the per-partial fetch loop, DOMParser, scanDoc,
  CORE_TABS, and the plugin-id allowlist. Search, keyboard nav, navigation,
  and the per-tab filter are unchanged.

Net: fewer requests and no client-side HTML parsing. Verified with a new
endpoint test in test_web_settings_ui.py (12 pass) and the headless-Chromium
test (tooltip, filter, search navigate + flash all green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
web_interface/static/v3/js/tooltips.js (1)

160-161: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the console.log from production.

This debug log will appear in the browser console for every page load. Consider removing it or gating it behind a debug flag.

♻️ Proposed fix
-
-    console.log('[Tooltips] controller registered');
 })();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web_interface/static/v3/js/tooltips.js` around lines 160 - 161, The debug
console log in the tooltips initialization is leaking into production output;
remove the `console.log('[Tooltips] controller registered')` from the
`tooltips.js` module or gate it behind an explicit debug flag used by the
controller setup. Keep the rest of the initialization IIFE unchanged and ensure
the `controller registered` message is not emitted on normal page loads.
web_interface/static/v3/app.css (1)

834-841: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add max-height and overflow-y: auto to #settings-search-results.

The dropdown has no height constraint. On smaller screens (e.g., a Raspberry Pi 7" touchscreen), a full result set can overflow the viewport with no way to scroll. The .ssr-group sticky positioning also needs a scroll container to function. Given the Raspberry Pi target, this is worth addressing.

♻️ Proposed fix
 `#settings-search-results` {
     background: var(--color-surface);
     border: 1px solid var(--color-border);
     border-radius: 0.5rem;
     box-shadow: var(--shadow-lg);
     z-index: 50;
     padding: 0.25rem;
+    max-height: min(60vh, 24rem);
+    overflow-y: auto;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web_interface/static/v3/app.css` around lines 834 - 841, The
`#settings-search-results` dropdown needs a height constraint and scroll container
support so it won’t overflow small screens and can enable sticky .ssr-group
behavior. Update the `#settings-search-results` rule in app.css to add a
max-height appropriate for the viewport and overflow-y: auto, keeping the
existing visual styling intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/test_web_settings_ui.py`:
- Around line 120-142: The search-index test in test_search_index_endpoint
currently covers general, display, and wifi fields but misses the durations tab,
so it won’t catch regressions there. Update the assertions in
test_search_index_endpoint to include a representative durations anchor from the
settings search index, and verify it has the same required label/help/tab
metadata as the other checked fields.

In `@web_interface/blueprints/pages_v3.py`:
- Around line 232-237: The settings search index is missing the Durations
section because `core_tabs` in `settings_search_index()` does not include
`_load_durations_partial()`. Update the `core_tabs` list to add a Durations
entry using the existing tab pattern and the `_load_durations_partial` loader so
its `setting-*` fields are included in the indexed pages.

In `@web_interface/static/v3/js/settings-search.js`:
- Around line 52-71: The failure path in buildIndex is caching an empty array in
window._settingsIndex, which causes later calls to short-circuit and never retry
after a transient fetch error. Update buildIndex so the .catch handler does not
leave a truthy failed cache behind; instead clear the cached index state and
reset buildPromise so a later call can refetch the index. Keep the retry
behavior tied to buildIndex, window._settingsIndex, and buildPromise.
- Around line 311-316: `filterScope` currently falls back to `document`, which
can make `applyTabFilter` hide setting fields across unrelated tabs. Update
`filterScope` to return only a real nearest container (or nothing) instead of
`document`, and add a guard in `applyTabFilter` so it skips filtering when no
valid tab/content scope is found. Use the existing `filterScope` helper and its
caller to keep the search limited to the current tab.
- Around line 8-17: Update the top-of-file comment in settings-search.js so it
matches the current implementation: the global search no longer fetches each
tab’s partial or scans window.installedPlugins, and it now uses the server-side
/v3/settings/search-index endpoint. Keep the per-tab filter description if still
accurate, but rewrite the global search and plugin-loading notes to describe the
JSON index-based flow and avoid mentioning the removed client-side approach.

---

Nitpick comments:
In `@web_interface/static/v3/app.css`:
- Around line 834-841: The `#settings-search-results` dropdown needs a height
constraint and scroll container support so it won’t overflow small screens and
can enable sticky .ssr-group behavior. Update the `#settings-search-results` rule
in app.css to add a max-height appropriate for the viewport and overflow-y:
auto, keeping the existing visual styling intact.

In `@web_interface/static/v3/js/tooltips.js`:
- Around line 160-161: The debug console log in the tooltips initialization is
leaking into production output; remove the `console.log('[Tooltips] controller
registered')` from the `tooltips.js` module or gate it behind an explicit debug
flag used by the controller setup. Keep the rest of the initialization IIFE
unchanged and ensure the `controller registered` message is not emitted on
normal page loads.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33962156-5162-4bd2-9d00-d1c331939eab

📥 Commits

Reviewing files that changed from the base of the PR and between 63a233f and a2fb3d1.

📒 Files selected for processing (14)
  • test/test_web_settings_ui.py
  • web_interface/blueprints/pages_v3.py
  • web_interface/static/v3/app.css
  • web_interface/static/v3/js/settings-search.js
  • web_interface/static/v3/js/tooltips.js
  • web_interface/templates/v3/base.html
  • web_interface/templates/v3/partials/_macros.html
  • web_interface/templates/v3/partials/backup_restore.html
  • web_interface/templates/v3/partials/display.html
  • web_interface/templates/v3/partials/durations.html
  • web_interface/templates/v3/partials/general.html
  • web_interface/templates/v3/partials/plugin_config.html
  • web_interface/templates/v3/partials/schedule.html
  • web_interface/templates/v3/partials/wifi.html

Comment thread test/test_web_settings_ui.py
Comment thread web_interface/blueprints/pages_v3.py
Comment thread web_interface/static/v3/js/settings-search.js Outdated
Comment thread web_interface/static/v3/js/settings-search.js
Comment thread web_interface/static/v3/js/settings-search.js
claude and others added 4 commits July 8, 2026 17:20
- pages_v3: include Durations tab in the search index so
  setting-durations-* fields are actually indexed
- test_web_settings_ui: assert setting-durations-clock is present in
  the search-index endpoint response
- settings-search.js: on index fetch failure, reset buildPromise
  instead of caching an empty (truthy) index so search can retry
- settings-search.js: filterScope returns null (not document) when no
  tab container matches, and the caller guards, so the per-tab filter
  can't hide fields across unrelated tabs
- settings-search.js: refresh the stale header comment to describe the
  server-side JSON index flow
- app.css: cap #settings-search-results height with overflow-y so the
  dropdown scrolls instead of overflowing small screens

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
Global search:
- Close the results dropdown on input blur (guarded, with a short delay)
  so it reliably dismisses when focus leaves — previously it could linger
  because the only outside-close was a document click that Alpine/HTMX
  handlers can swallow.
- Clear the query text after navigating to a result so refocusing the box
  doesn't re-open stale results.
- Also dismiss on htmx:afterSwap (tab changes / navigation).

Per-tab filter (now on plugin tabs too):
- Render the shared settings_filter box in the plugin Configuration panel.
  It auto-wires: the delegated input handler and filterScope already target
  .plugin-config-tab.
- Teach applyTabFilter to reveal matches inside collapsed nested sections
  (render_nested_section defaults them shut), hide nested-section wrappers
  with no matches, and restore the original collapsed layout when cleared
  (only re-collapsing sections the filter itself opened).
- Count a visible nested-section as content for its parent heading so the
  heading isn't hidden while a subsection below still has matches.

Adds a plugin-config render test (filter box + nested anchors + tooltips).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
The dropdown could stay open after clicking away because the only
outside-click close was a bubble-phase document listener. The v3 UI is
one Alpine app() component full of HTMX/Alpine/widget click handlers;
when a click lands inside an element that calls stopPropagation(), the
event never bubbles to document and the close never runs.

- Replace the bubble-phase document 'click' close with a capture-phase
  'pointerdown' listener scoped to #settings-search-wrap. Capture runs
  before any bubbling stopPropagation can swallow the event, so it always
  fires; pointerdown also covers touch on the Pi screen. Clicking a result
  stays inside the wrap, so selection is unaffected.
- Guard the debounced input handler so a delayed render can't re-open the
  box after focus has left (type-then-click-away race).

Keeps the existing blur / Escape / htmx:afterSwap closes as secondary paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
.hidden has no effect in this app: app.css is a hand-picked utility
subset (no Tailwind build step) and never defines .hidden { display:
none }. openResults()/closeResults() only toggled the class, so the
dropdown stayed rendered (display: block) even once closeResults()
ran - confirmed via computed style in a headless browser. Set
style.display directly, matching the fallback already used by
revealNode()/collapseNode() elsewhere in this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ChuckBuilds ChuckBuilds merged commit 978a03b into main Jul 9, 2026
8 checks passed
@ChuckBuilds ChuckBuilds deleted the claude/web-ui-tooltips-search-36s3b9 branch July 9, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants